import wx
import os, sqlite3, logging, copy
import httplib, select, threading, signal
from simple_parser import SimpleParser

def getFileList(name,address,ip,port,callback):
    """
    Gets a file listing from the specified computer, does 
    a wxWidgets thread safe call to the last argument, 'callback'
    """
    con = httplib.HTTPConnection(ip, port)
    con.request("GET", "/")
    content = con.getresponse()
    try:
        ps = SimpleParser(content)
        ps.parse()
        paths = ps.getPaths()
        wx.CallAfter(callback, paths, address)
    except ExpatError, e:
        logging.warning('parsing failed'+str(e))
    except ParseError, e:
        logging.warning('parsing failed'+str(e))

def downloadFile(ip, port, path, destination_dir, chunk, completed):
    """
    Downloads the specified file to destination_dir, calls 'chunk' per
    kilobyte, calls completed when done.
    
    All callbacks are done using wx.CallAfter
    """
    dest = None
    if len(os.path.basename(path)):
        dest = file(os.path.join(destination_dir, 
            os.path.basename(path)),'w')
    else:
        dest = file(os.tempnam(destination_dir),'w')
    logging.debug('Starting download')
    con = httplib.HTTPConnection(ip, port)
    con.request("GET", path)
    response = con.getresponse()
    
    for chunk in range(0, response.length, 1024):
        next = response.read(1024)
        dest.write(next)
        wx.CallAfter(chunk)
    
    dest.close()
    wx.CallAfter(completed)
    
class Main(wx.Frame):
    def __init__(self, parent, id, title, pid, env):
        wx.Frame.__init__(self,parent,wx.ID_ANY,title, size=(700,400),
            style=wx.NO_FULL_REPAINT_ON_RESIZE | wx.DEFAULT_FRAME_STYLE)
        self.pid = pid
        self.env = env
        self.Show(True)
        self.menu = wx.Menu()
        self.current_directory = os.getcwd()
        self.toolbar = wx.ToolBar(self,style=wx.TB_HORIZONTAL|wx.TB_TEXT|wx.TB_TOP)
        self.bv_sizer = wx.BoxSizer(wx.VERTICAL)
        self.bh_sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        self.browse_tree = wx.TreeCtrl(self,style=wx.BORDER_SUNKEN|wx.TR_HIDE_ROOT)
        self.share_tree = wx.TreeCtrl(self,style=wx.BORDER_SUNKEN)
        self.progress = wx.ListCtrl(self, style=wx.LC_REPORT)

        self.art = wx.ArtProvider()

        self.SetSizer(self.bv_sizer)
        self.bv_sizer.Add(self.toolbar,0, wx.ALIGN_TOP)
        self.bv_sizer.Add(self.bh_sizer,2,wx.EXPAND)
        self.bv_sizer.Add(self.progress,1,wx.ALIGN_BOTTOM|wx.EXPAND)

        self.bh_sizer.Add(self.share_tree,1,wx.EXPAND|wx.ALIGN_LEFT)
        self.bh_sizer.Add(self.browse_tree,1,wx.EXPAND|wx.ALIGN_RIGHT)

        self.share_tree.AddRoot('Shared Files') # XXX
        self.browse_tree.AddRoot('') # XXX
        self.progress.InsertColumn(0,"File")
        self.progress.InsertColumn(1,"Progress")

        ##
        # self.toolbar.AddLabelTool(self, id, label, bitmap
        ## 
        self.DOWNBUT_ID = wx.NewId()
        self.toolbar.AddLabelTool(self.DOWNBUT_ID, "Download", self.art.GetBitmap(wx.ART_GO_DOWN))
        self.ADDBUT_ID = wx.NewId()
        self.toolbar.AddLabelTool(self.ADDBUT_ID, "Share File", self.art.GetBitmap(wx.ART_NEW))
        self.DELBUT_ID = wx.NewId()
        self.toolbar.AddLabelTool(self.DELBUT_ID, "Stop Sharing", self.art.GetBitmap(wx.ART_DELETE))
        self.QBUT_ID = wx.NewId()
        self.toolbar.AddLabelTool(self.QBUT_ID, "Quit", self.art.GetBitmap(wx.ART_QUIT))

        self.Bind(wx.EVT_TOOL, self.quit, id=self.QBUT_ID)
        self.Bind(wx.EVT_CLOSE, self.quit)
        self.Bind(wx.EVT_TOOL, self._add_file, id=self.ADDBUT_ID)
        self.Bind(wx.EVT_TOOL, self._rem_file, id=self.DELBUT_ID)
        self.Bind(wx.EVT_TOOL, self.download, id=self.DOWNBUT_ID)
        
        
        self.browse_tree.Bind(wx.EVT_TREE_SEL_CHANGED, self._b_t_change, id=self.browse_tree.Id)
        self.share_tree.Bind(wx.EVT_TREE_SEL_CHANGED, self._s_t_change, id=self.share_tree.Id)

        self.Layout()
        self.prepopulate()
        self.start_zeroconf()

    def FIXME(self, *args):
        logging.debug("FIXME called: %s" % args)

    def download(self, *args):
        logging.debug("Download button args: %s" % args)

    def _add_file(self, *args):
        logging.debug("Add file called: %s" % args)
        chooser = wx.FileDialog(self, "Add files", self.current_directory, "", "*", wx.OPEN)
        if chooser.ShowModal() == wx.ID_OK:
            path = chooser.GetPath()
            if os.path.exists(path):
                self.current_directory = os.path.dirname(path)
                if os.path.isdir(path):
                    logging.error("%s is a directory, which we can't add yet" % path)
                else:
                    conn = sqlite3.connect(os.path.join(self.env.get('CONFIGDIR'), self.env.get('CONFIGLOC'), self.env.get('DBNAME')) )
                    cursor = conn.cursor()
                    
                    # FIXME: this bit is getting copied and pasted around in the various
                    # GUIs, add it to the 'env'?
                    cursor.execute('INSERT INTO ' + self.env.get('DBTABLE') + ' values ( ?, ?, ? ) ', (path, False, False))
                    conn.commit()
                    self.share_tree.AppendItem(self.share_tree.RootItem, path)
            else:
                logging.error("%s is not a proper location, what went wrong?" % path)
                    
        chooser.Destroy()

    def _rem_file(self, *args):
        logging.debug("Rem file called: %s" % args)
        
    def _s_t_change(self, event):
        logging.debug("share_tree" % event)

    def _b_t_change(self, event):
        logging.debug("browse_tree" % event)

    def _avahi_add_server(self, name, address, ip, port):
        t=threading.Thread(target=getFileList,args=(name,address,ip,port, self._add_remote_files))
        t.setDaemon(True)
        t.start()

    def _add_remote_files(self, paths, address):
        root = self.browse_tree.AppendItem(self.browse_tree.RootItem, address)
        for path in paths:
            self.browse_tree.AppendItem(root, path)
            logging.debug("added %s" % path)

        self.browse_tree.ExpandAll()
        
    def start_zeroconf(self):
        try:
            from avahi_search import AvahiSearch
            import gobject, dbus.glib
            gobject.threads_init()
            dbus.glib.threads_init()

            self.search = AvahiSearch('_fhttp._tcp')
            self.search.register_service_handler(self._avahi_add_server)
        except ImportError, e:
            logging.error(str(e)+"\tStarting pybonjour")
            try:
                import zero_listener    
            except ImportError:
                logging.critical("Couldn't connect to zeroconf, sorry")
        
    def prepopulate(self):
        conn = sqlite3.connect( os.path.expanduser(os.path.join(self.env.get('CONFIGDIR'), self.env.get('CONFIGLOC'), self.env.get('DBNAME'))) )
        cursor = conn.cursor()
        paths = [x[0] for x in cursor.execute(self.env.get('SELECT_ALL_PATHS')).fetchall()]

        for path in paths:
            self.share_tree.AppendItem(self.share_tree.RootItem, path)

        self.share_tree.ExpandAll() # Show

    def quit(self,*args):
        self.Destroy()
        os.kill(self.pid, signal.SIGTERM)

def Run(pid,env):
    """
    Start the wx GUI
    """
    app = wx.App(0)
    frame = Main(None, 100, "Share Files", pid, copy.deepcopy(env))
    app.MainLoop()
    
if __name__ == "__main__":
    env = {}
    env['PORT'] = 6899
    env['CONFIGDIR'] = '~/.config/'
    env['CONFIGLOC'] = 'ffshare'
    env['DBNAME'] = 'files.db'
    env['DOWNLOADTO'] = '~/Desktop/'
    env['DBTABLE'] = env['CONFIGLOC']+'_files'
    env['SELECT_ALL_PATHS'] = 'select path from ' + env['DBTABLE'] + ';'

    Run(0, env)
